<script lang="ts" setup>
import { onMounted, ref } from 'vue'

const startButton = ref()
const callButton = ref()
const hangupButton = ref()
// 设置两个video，分别显示本地视频流和远端视频流
const localVideo = ref<HTMLVideoElement>();
const remoteVideo = ref<HTMLVideoElement>();
const startBtnDisabled = ref(false)
const callBtnDisabled = ref(false)
const hangupBtnDisabled = ref(false)

// 传输视频，不传输音频
const mediaStreamConstraints = { video: true, audio: false };

// 设置只交换视频
const offerOptions: RTCOfferOptions = { offerToReceiveVideo: true };

let startTime: number | null;

let localStream: MediaStream;
let remoteStream: MediaStream | null = null;
// 建立两个对等连接对象，分表代表本地和远端
let localPeerConnection: RTCPeerConnection | null
let remotePeerConnection: RTCPeerConnection | null;

onMounted(() => {
  callBtnDisabled.value = true;
  hangupBtnDisabled.value = true;
})


function gotLocalMediaStream(mediaStream: MediaStream) {
  localVideo.value!.srcObject = mediaStream;
  localStream = mediaStream;
  trace('Received local stream.');
  callBtnDisabled.value = false;
}

function handleLocalMediaStreamError(error: Error) {
  trace(`navigator.getUserMedia error: ${error.toString()}.`);
}

function gotRemoteMediaStream(event: RTCTrackEvent) {
  if (event.streams && event.streams[0]) {
    remoteVideo.value!.srcObject = event.streams[0];
  } else {
    if (!remoteStream) {
      remoteStream = new MediaStream();
      remoteVideo.value!.srcObject = remoteStream;
    }
    remoteStream.addTrack(event.track);
  }
  trace('Remote peer connection received remote stream.');
}

function logVideoLoaded(event: Event) {
  const video = event.target as HTMLVideoElement;
  trace(`${video.id} videoWidth: ${video.videoWidth}px, ` +
      `videoHeight: ${video.videoHeight}px.`);
}

function logResizedVideo(event: Event) {
  logVideoLoaded(event);
  if (startTime) {
    const elapsedTime = window.performance.now() - startTime;
    startTime = null;
    trace(`Setup time: ${elapsedTime.toFixed(3)}ms.`);
  }
}

function handleConnection(event: RTCPeerConnectionIceEvent) {
  const peerConnection = event.target as RTCPeerConnection;
  const iceCandidate = event.candidate;

  if (iceCandidate) {
    const newIceCandidate = new RTCIceCandidate(iceCandidate);
    const otherPeer = getOtherPeer(peerConnection);

    otherPeer?.addIceCandidate(newIceCandidate)
        .then(() => {
          handleConnectionSuccess(peerConnection);
        }).catch((error) => {
      handleConnectionFailure(peerConnection, error);
    });

    trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
        `${event.candidate.candidate}.`);
  }
}

function handleConnectionSuccess(peerConnection: RTCPeerConnection) {
  trace(`${getPeerName(peerConnection)} addIceCandidate success.`);
}

function handleConnectionFailure(peerConnection: RTCPeerConnection, error: Error) {
  trace(`${getPeerName(peerConnection)} failed to add ICE Candidate:\n` + `${error.toString()}.`);
}

function handleConnectionChange(event: Event) {
  const peerConnection = event.target as RTCPeerConnection;
  console.log('ICE state change event: ', event);
  trace(`${getPeerName(peerConnection)} ICE state: ` + `${peerConnection?.iceConnectionState}.`);
}

function setSessionDescriptionError(error: Error) {
  trace(`Failed to create session description: ${error.toString()}.`);
}

function setDescriptionSuccess(peerConnection: RTCPeerConnection, functionName: string) {
  const peerName = getPeerName(peerConnection);
  trace(`${peerName} ${functionName} complete.`);
}

function setLocalDescriptionSuccess(peerConnection: RTCPeerConnection) {
  setDescriptionSuccess(peerConnection, 'setLocalDescription');
}

function setRemoteDescriptionSuccess(peerConnection: RTCPeerConnection) {
  setDescriptionSuccess(peerConnection, 'setRemoteDescription');
}

function createdOffer(description: RTCSessionDescriptionInit) {
  trace(`Offer from localPeerConnection:\n${description.sdp}`);

  trace('localPeerConnection setLocalDescription start.');
  localPeerConnection?.setLocalDescription(description)
      .then(() => {
        localPeerConnection && setLocalDescriptionSuccess(localPeerConnection);
      }).catch(setSessionDescriptionError);

  trace('remotePeerConnection setRemoteDescription start.');
  remotePeerConnection?.setRemoteDescription(description)
      .then(() => {
        remotePeerConnection && setRemoteDescriptionSuccess(remotePeerConnection);
      }).catch(setSessionDescriptionError);

  trace('remotePeerConnection createAnswer start.');
  remotePeerConnection?.createAnswer()
      .then(createdAnswer)
      .catch(setSessionDescriptionError);
}

function createdAnswer(description: RTCSessionDescriptionInit) {
  trace(`Answer from remotePeerConnection:\n${description.sdp}.`);

  trace('remotePeerConnection setLocalDescription start.');
  remotePeerConnection?.setLocalDescription(description)
      .then(() => {
        remotePeerConnection && setLocalDescriptionSuccess(remotePeerConnection);
      }).catch(setSessionDescriptionError);

  trace('localPeerConnection setRemoteDescription start.');
  localPeerConnection?.setRemoteDescription(description)
      .then(() => {
        localPeerConnection && setRemoteDescriptionSuccess(localPeerConnection);
      }).catch(setSessionDescriptionError);
}


async function startAction() {
  startBtnDisabled.value = true;
  try {
    const stream = await navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
    gotLocalMediaStream(stream);
  } catch (e) {
    handleLocalMediaStreamError(e as Error)
  }
  trace('Requesting local stream.');
}

// 创建对等连接
function callAction() {
  callBtnDisabled.value = true;
  hangupBtnDisabled.value = false;

  trace('Starting call.');
  startTime = window.performance.now();

  const videoTracks = localStream.getVideoTracks();
  const audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace(`Using video device: ${videoTracks[0].label}.`);
  }
  if (audioTracks.length > 0) {
    trace(`Using audio device: ${audioTracks[0].label}.`);
  }
  // 服务器配置
  const servers: RTCConfiguration | undefined = undefined;

  localPeerConnection = new RTCPeerConnection(servers);
  trace('Created local peer connection object localPeerConnection.');

  localPeerConnection.addEventListener('icecandidate', handleConnection);
  localPeerConnection.addEventListener('iceconnectionstatechange', handleConnectionChange);
  localPeerConnection.addEventListener('track', (track) => {
    console.log('local add track', track)
  });

  remotePeerConnection = new RTCPeerConnection(servers);
  trace('Created remote peer connection object remotePeerConnection.');

  remotePeerConnection.addEventListener('icecandidate', handleConnection);
  remotePeerConnection.addEventListener('iceconnectionstatechange', handleConnectionChange);
  remotePeerConnection.addEventListener('track', gotRemoteMediaStream);

  for (const track of localStream.getTracks()) {
    localPeerConnection.addTrack(track);
  }
  trace('Added local stream to localPeerConnection.');

  trace('localPeerConnection createOffer start.');
  localPeerConnection.createOffer(offerOptions)
      .then(createdOffer).catch(setSessionDescriptionError);
}

function hangupAction() {
  localPeerConnection?.close();
  remotePeerConnection?.close();
  localPeerConnection = null;
  remotePeerConnection = null;
  hangupBtnDisabled.value = true;
  callBtnDisabled.value = false;
  trace('Ending call.');
}

function getOtherPeer(peerConnection: RTCPeerConnection) {
  return (peerConnection === localPeerConnection) ? remotePeerConnection : localPeerConnection;
}

function getPeerName(peerConnection: RTCPeerConnection) {
  return (peerConnection === localPeerConnection) ? 'localPeerConnection' : 'remotePeerConnection';
}

function trace(text: string) {
  text = text.trim();
  const now = (window.performance.now() / 1000).toFixed(3);
  console.log(now, text);
}

</script>

<template>
  <div>
    <el-button ref="startButton" @click="startAction" :disabled="startBtnDisabled">开始</el-button>
    <el-button ref="callButton" @click="callAction" :disabled="callBtnDisabled">调用</el-button>
    <el-button ref="hangupButton" @click="hangupAction" :disabled="hangupBtnDisabled">停止录制</el-button>
  </div>
  <div>
    <video ref="localVideo" @loadedmetadata="logVideoLoaded" autoplay controls class="local"/>
    <video ref="remoteVideo" @loadedmetadata="logVideoLoaded" @onresize="logResizedVideo" autoplay controls
           class="remote"/>
  </div>
</template>

<style scoped>
video {
  max-width: 100%;
  width: 320px;
}
</style>
